package cn.s1995.sloth.app.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.entity.Prod;
import cn.s1995.sloth.bizs.entity.ProdGroupReference;
import cn.s1995.sloth.bizs.entity.Shop;
import cn.s1995.sloth.bizs.exception.BizException;
import cn.s1995.sloth.bizs.oo.req.ProdSearchREQ;
import cn.s1995.sloth.bizs.oo.vo.ProdDetailVO;
import cn.s1995.sloth.bizs.oo.vo.ProdGroupReferenceVO;
import cn.s1995.sloth.bizs.oo.vo.ProdListVO;
import cn.s1995.sloth.bizs.service.*;
import cn.s1995.sloth.bizs.service.common.CacheService;
import cn.s1995.sloth.bizs.util.ImageSearchUtil;
import cn.s1995.sloth.bizs.util.PageUtil;
import cn.s1995.sloth.common.core.constant.enums.hint.Hint;
import cn.s1995.sloth.common.core.util.R;
import cn.s1995.sloth.common.security.annotation.Inner;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/m/prod")
@Tag(description = "Prod", name = "APP-产品模块")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ApiProdController {

	private final UserHistorySearchService userHistorySearchService;

	private final ProdService prodService;

	private final CacheService cacheService;

	private final ProdGroupReferenceService prodGroupReferenceService;

	private final ImageSearchUtil imageSearchUtil;

	@Resource
	private HttpServletRequest request;

	@Inner(false)
	@ApiOperationSupport(order = 1)
	@Operation(summary = "分页查询", description = "分页查询")
	@GetMapping("/page")
	public R<Page<ProdListVO>> page(@ParameterObject Page page, @ParameterObject ProdSearchREQ req) {
		Long uid = cacheService.getUidByToken(request.getHeader(HttpHeaders.AUTHORIZATION));
		if (StrUtil.isNotBlank(req.getSearch()) && ObjectUtil.isNotNull(uid)) {
			userHistorySearchService.updateHistory(uid, req.getSearch());
		}
		// 设置默认排序
		PageUtil.setDefaultOrders(page, "t");
		return R.ok(prodService.getPage(page, req, uid));
	}

	@Inner(false)
	@ApiOperationSupport(order = 2)
	@Operation(summary = "以图搜图（上传文件方式）", description = "以图搜图（上传文件方式）")
	@PostMapping("/search/image/pic")
	public R<List<ProdListVO>> searchByPic(@RequestPart("file") MultipartFile file) {
		String suffix = FileUtil.extName(file.getOriginalFilename());
		if (!ImageSearchUtil.isPicture(suffix))
			throw new BizException(Hint.NON_ONLY_IMAGE);
		try {
//			log.info("压缩前" + LocalDateTime.now().toString());
			InputStream adjustInputStream = imageSearchUtil.adjust(file, suffix);
//			InputStream adjustInputStream = imageSearchUtil.adjust(inputStream, suffix, file.getSize());
//			log.info("压缩后" + LocalDateTime.now().toString());
			try (InputStream is = adjustInputStream == null ? file.getInputStream() : adjustInputStream) {
//				log.info("请求阿里前" + LocalDateTime.now().toString());
				List<String> productIds = imageSearchUtil.searchByPic(is);
//				log.info("去重/筛选后" + LocalDateTime.now().toString());
				if (ObjectUtil.isNotEmpty(productIds)) {
					Long uid = cacheService.getUidByToken(request.getHeader(HttpHeaders.AUTHORIZATION));
					ProdSearchREQ req = new ProdSearchREQ();
					req.setProductIds(productIds);
					List<ProdListVO> prodList = prodService.getList(req, uid);
					return R.ok(prodList);
				}
			}
			return R.ok(Collections.emptyList());
		} catch (Exception e) {
			return R.ok(Collections.emptyList());
		}
	}

	@Inner(false)
	@ApiOperationSupport(order = 3)
	@Operation(summary = "以图搜图（url方式）", description = "以图搜图（url方式）")
	@Parameters({
			@Parameter(name = "url", description = "图片地址", required = true, in = ParameterIn.DEFAULT),
	})
	@PostMapping("/search/image/url")
	public R<List<ProdListVO>> searchByPicUrl(@RequestParam(required = true) String url) {
		String picName = url.substring(url.lastIndexOf("/") + 1, url.length());
		String suffix = FileUtil.extName(picName);
		if (!ImageSearchUtil.isPicture(suffix))
			throw new BizException(Hint.NON_ONLY_IMAGE);
		try (InputStream openStream = new URL(url).openStream()) {
			MultipartFile file = new MockMultipartFile(picName, null, "image/*", openStream);
			try (InputStream adjustInputStream = imageSearchUtil.adjust(file, suffix);) {
//				InputStream adjustInputStream = imageSearchUtil.adjust(inputStream, suffix, file.getSize());
				try (InputStream is = adjustInputStream == null ? file.getInputStream() : adjustInputStream) {
					List<String> productIds = imageSearchUtil.searchByPic(is);
					if (ObjectUtil.isNotEmpty(productIds)) {
						Long uid = cacheService.getUidByToken(request.getHeader(HttpHeaders.AUTHORIZATION));
						ProdSearchREQ req = new ProdSearchREQ();
						req.setProductIds(productIds);
						List<ProdListVO> prodList = prodService.getList(req, uid);
						return R.ok(prodList);
					}
				}
			}
			return R.ok(Collections.emptyList());
		} catch (Exception e) {
			return R.ok(Collections.emptyList());
		}
	}

	@Inner(false)
	@ApiOperationSupport(order = 4)
	@Operation(summary = "分页获取分组下的产品", description = "分页获取分组下的产品")
	@Parameters({
			@Parameter(name = "id", description = "分组id", required = true, in = ParameterIn.PATH),
	})
	@GetMapping("/group/{id}")
	public R<Page<ProdListVO>> getGroupPage(@PathVariable("id") Long id, @ParameterObject Page page) {
		MPJLambdaWrapper<ProdGroupReference> wrapper = new MPJLambdaWrapper<ProdGroupReference>()
				.selectAll(ProdGroupReference.class)
				.selectAssociation(Prod.class, ProdGroupReferenceVO::getProd)
				.leftJoin(Prod.class, Prod::getId, ProdGroupReference::getProdId)
				.leftJoin(Shop.class, Shop::getId, Prod::getShopId)
				.eq(ProdGroupReference::getGroupId, id)
				.eq(ProdGroupReference::getState, BizCst.CommonEnableState.enable)
				.eq(Prod::getIsSale, BizCst.CommonBool.yes)
				.eq(Shop::getState, BizCst.CommonEnableState.enable);
		Page resultPage = prodGroupReferenceService.selectJoinPage(page, wrapper);
		List<ProdGroupReferenceVO> records = resultPage.getRecords();
		// 回显收藏状态
		if (ObjectUtil.isNotEmpty(records)) {
			List<Prod> prodList = records.stream().map(e -> e.getProd()).collect(Collectors.toList());
			Long uid = cacheService.getUidByToken(request.getHeader(HttpHeaders.AUTHORIZATION));
			List<ProdListVO> resultList = prodService.setCollectStatus(uid, prodList);
			resultPage.setRecords(resultList);
		}
		return R.ok(resultPage);
	}

	/**
	 * 通过id查询产品
	 *
	 * @param id id
	 * @return R
	 */
	@Inner(false)
	@ApiOperationSupport(order = 5)
	@Operation(summary = "通过id查询", description = "通过id查询")
	@GetMapping("/detail/{id}")
	public R<ProdDetailVO> getById(@PathVariable("id") Long id) {
		// 支持查询被逻辑删除的数据
		Prod prod = prodService.getDeletedProd(id);
		ProdDetailVO vo = prodService.convertToDetailVO(prod, cacheService.getUidByToken(request.getHeader(HttpHeaders.AUTHORIZATION)));
		return R.ok(vo);
	}


	@ApiOperationSupport(order = 20)
	@Operation(summary = "收藏产品", description = "收藏产品")
	@Parameters({
			@Parameter(name = "id", description = "产品id", required = true, in = ParameterIn.PATH),
	})
	@PostMapping("/collect/{id}")
	public R collectProd(@PathVariable("id") Long id) {
		return prodService.collect(id);
	}

	/**
	 * 构建查询条件
	 *
	 * @param req
	 * @return
	 */
	@NotNull
	private static MPJLambdaWrapper<Prod> buildQueryWrapper(ProdSearchREQ req) {
		MPJLambdaWrapper<Prod> wrapper = new MPJLambdaWrapper<Prod>();
		wrapper.eq(Prod::getIsSale, BizCst.CommonBool.yes);
		wrapper.eq(Objects.nonNull(req.getShopId()), Prod::getShopId, req.getShopId());
		wrapper.eq(Objects.nonNull(req.getCategoryId()), Prod::getCategoryId, req.getCategoryId());
		wrapper.like(Objects.nonNull(req.getCategoryId()), Prod::getCategoryPath, req.getCategoryId());
		wrapper.eq(Objects.nonNull(req.getIsCustomizable()), Prod::getIsCustomizable, req.getIsCustomizable());
		wrapper.eq(Objects.nonNull(req.getIsRecommend()), Prod::getIsRecommend, req.getIsRecommend());
		wrapper.eq(Objects.nonNull(req.getIsHot()), Prod::getIsHot, req.getIsHot());

		wrapper.and(
				StrUtil.isNotEmpty(req.getSearch()),
				w -> w.like(Prod::getName, req.getSearch())
						.or()
						.like(Prod::getBrief, req.getSearch())
						.or()
						.like(Prod::getSn, req.getSearch())
						.or()
						.like(Prod::getModel, req.getSearch())
		);
		// 关联shop，排除被禁用的shop的产品
		wrapper.eq(Shop::getState, BizCst.CommonEnableState.enable);
		wrapper.leftJoin(Shop.class, Shop::getId, Prod::getShopId);
		return wrapper;
	}
}
